#pragma once

#include <functional>
#include <string>
#include <vector>
#include <mutex>
#include <chrono>
#include "Utils.h"

namespace tool {
namespace Log {

// UTC milliseconds
int64_t TimeStamp();

//time stamp to string with custom format
std::string  TimeToString(int64_t timeStamp, const std::string& format = "%Y-%m-%d %H:%M:%S");

// time stamp with milliseconds precision
std::string  TimeMsToString(int64_t timeStamp, const std::string& format = "%Y-%m-%d %H:%M:%S.%g");

/*!
 * @brief GetFilesFromDir 获取指定路径下的文件名
 * @param dirName 路径名，请注意，在windows下，支持通配符("./ *.txt")，在linux下不支持
 * @return
 */
std::vector<std::string> GetFilesFromDir(const std::string& dirName);

void CreatePathForFile(const std::string& fileName);


//file
template<size_t N, typename CharT = char>
inline constexpr const CharT* FindFirstOf(const CharT(&arr)[N], CharT c)
{
    for (size_t i = 0;i < N;++i)
    {
        if (arr[i] == c)
            return arr + i;
    }
    return arr;
}

template<size_t N, typename CharT = char>
inline constexpr const CharT* FindLastOf(const CharT(&arr)[N], CharT c)
{
    for (int i = N - 1;i >= 0;--i)
    {
        if (arr[i] == c)
            return arr + i;
    }
    return arr;
}

// 用于编译期计算带路径字符串的名字
#ifdef _WIN32
#define SHORT_FILE (tool::Log::FindLastOf(__FILE__,'\\')+1)
#else
#define SHORT_FILE (tool::Log::FindLastOf(__FILE__, '/') + 1)
#endif

enum LogLevel
{
    LEVEL_TRACE,
    LEVEL_DEBUG,
    LEVEL_INFO,
    LEVEL_WARNING,
    LEVEL_ERROR,
    LEVEL_SUCCESS,
    LEVEL_UNDEFINED
};

}
}//namespace tool::Log

//#define LOG_NO_DEBUG

#define USE_ASYNC_LOG



#ifndef USE_ASYNC_LOG
#include "SyncLog.h"
#if defined(LOG_NO_OUTPUT)
#   define LOG(...)
#else
#   define LOG(...) tool::Log::CSysLog::GetInstance()->ShowMessage(__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_TRACE)
#   define LOG_T(...)
#else
#   define LOG_T(format , ...) tool::Log::AsyncLog::GetInstance()->ShowMessage(tool::Log::LEVEL_TRACE,__func__,__LINE__,__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_DEBUG)
#   define LOG_D(...)
#else
#   define LOG_D(...) tool::Log::CSysLog::GetInstance()->ShowMessage(tool::Log::LEVEL_DEBUG,__func__,__LINE__,__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_INFO)
#   define LOG_I(...)
#else
#   define LOG_I(...) tool::Log::CSysLog::GetInstance()->ShowMessage(tool::Log::LEVEL_INFO,__func__,__LINE__,__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_WARNING)
#   define LOG_W(...)
#else
#   define LOG_W(...) tool::Log::CSysLog::GetInstance()->ShowMessage(tool::Log::LEVEL_WARNING,__func__,__LINE__,__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_ERROR)
#   define LOG_E(...)
#else
#   define LOG_E(...) tool::Log::CSysLog::GetInstance()->ShowMessage(tool::Log::LEVEL_ERROR,__func__,__LINE__,__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_SUCCESS)
#   define LOG_S(...)
#else
#   define LOG_S(...) tool::Log::CSysLog::GetInstance()->ShowMessage(tool::Log::LEVEL_SUCCESS,__func__,__LINE__,__VA_ARGS__)
#endif

#else

#include "AsyncLog.h"

#define LOG_FORMAT(level) tool::Log::LogFormat(SHORT_FILE,__LINE__,__func__,level)

#if defined(LOG_NO_OUTPUT)
#   define LOG(...)
#else
#   define LOG(format, ...)   tool::Log::AsyncLog::GetInstance()->ShowMessage(format,##__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_TRACE)
#   define LOG_T(...)
#else
#   define LOG_T(format , ...) tool::Log::AsyncLog::GetInstance()->ShowMessage(LOG_FORMAT(tool::Log::LEVEL_TRACE),format,##__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_DEBUG)
#   define LOG_D(...)
#else
#   define LOG_D(format , ...) tool::Log::AsyncLog::GetInstance()->ShowMessage(LOG_FORMAT(tool::Log::LEVEL_DEBUG),format,##__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_INFO)
#   define LOG_I(...)
#else
#   define LOG_I(format , ...) tool::Log::AsyncLog::GetInstance()->ShowMessage(LOG_FORMAT(tool::Log::LEVEL_INFO),format,##__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_WARNING)
#   define LOG_W(...)
#else
#   define LOG_W(format , ...) tool::Log::AsyncLog::GetInstance()->ShowMessage(LOG_FORMAT(tool::Log::LEVEL_WARNING),format,##__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_ERROR)
#   define LOG_E(format , ...)
#else
#   define LOG_E(format , ...) tool::Log::AsyncLog::GetInstance()->ShowMessage(LOG_FORMAT(tool::Log::LEVEL_ERROR),format,##__VA_ARGS__)
#endif

#if defined(LOG_NO_OUTPUT) || defined(LOG_NO_SUCCESS)
#   define LOG_S(...)
#else
#   define LOG_S(format , ...) tool::Log::AsyncLog::GetInstance()->ShowMessage(LOG_FORMAT(tool::Log::LEVEL_SUCCESS),format,##__VA_ARGS__)
#endif


#endif



inline void InstallLogMessageScreenState(bool state)
{
#ifndef USE_ASYNC_LOG
    tool::Log::CSysLog::GetInstance()->setScreenState(state);
#else
    tool::Log::AsyncLog::GetInstance()->setScreenState(state);
#endif
}

inline void InstallLogMessageFileState(bool state, const std::string& fileName = "")
{
#ifndef USE_ASYNC_LOG
    tool::Log::CSysLog::GetInstance()->setFileState(state, fileName);
#else
    tool::Log::AsyncLog::GetInstance()->setFileState(state, fileName);
#endif
}

inline void InstallLogMessageHandler(std::function<void(const char* data, int size)> writer,
                                     std::function<void()> flusher = nullptr,
                                     std::function<void()> closer = nullptr)
{
#ifndef USE_ASYNC_LOG
    tool::Log::CSysLog::GetInstance()->installMessageCallback(writer);
#else
    tool::Log::AsyncLog::GetInstance()->installMessageCallback(writer, flusher, closer);
#endif
}

inline void InstallLogMessageFormat(const char* data)
{
#ifndef USE_ASYNC_LOG
    (void)data;
#else
    tool::Log::LogFormat::SetFormatString(data);
#endif
}

inline void InitLogMessage(int interval_ms = 3000)
{
#ifndef USE_ASYNC_LOG
    (void)interval_ms;
#else
    tool::Log::AsyncLog::GetInstance()->Start(interval_ms);
#endif
}


namespace tool {
//测试耗时用
class Timer {
public:
    Timer(const char* word)
        :m_begin(std::chrono::high_resolution_clock::now())
        , m_word(word)
        ,m_enable(true)
    {}
    ~Timer() {
        if (m_enable) {
            int64_t duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - m_begin).count();
            int dur_ms = duration / 1000, dur_us = duration % 1000;
            LOG_D("%s : %d ms %d us", m_word, dur_ms, dur_us);
        }
    }

    void MakeDisble()
    {
        m_enable = false;
    }
private:
    std::chrono::high_resolution_clock::time_point   m_begin;
    const char* m_word;
    bool        m_enable;
};
}
